﻿/*
 * Authors:    Kęstutis Džiautas
 *             Edmundas Matusevičius
 * University: VU, MIF, SE, 4 course
 * Date:       2010-10-23
 * Version:    1.0
 */

using System;
using System.Collections.Generic;
using System.IO;

namespace LexAnalizer
{
    public class LexemAnalizer
    {
        public LexemAnalizer(string codeFileName)
        {
            AutomatonStates autoStates = new AutomatonStates();
            this.statesTree = autoStates.getStatesTree();

            // if states tree empty, then finish program
            if (this.statesTree == null) {
                Console.WriteLine("States file does not exist.");
                Environment.Exit(0);
            }

            this.codeFileReader = new StreamReader(codeFileName);
        }
        /// <summary>
        /// Get non blank char
        /// </summary>
        private void getCharNB()
        {
            if (!peek)
            {
                this.ch = codeFileReader.Peek();
            } else if ((this.ch == 34) || (this.ch == 39)) {
                codeFileReader.Read();
                this.ch = codeFileReader.Peek();
            }

            while (true)
            {
                // \t, \n, \r, space
                if ((ch != 9) && (ch != 10) && (ch != 13) && (ch != 32))
                {
                    break;
                }
                codeFileReader.Read();
                this.ch = codeFileReader.Peek();
            }
        }
        /// <summary>
        /// Do all actions in state
        /// </summary>
        private void doAction(string action, ref int lexNum, ref string lexInf)
        {
            string[] actions = action.Split(';'),
                     outputs;
            foreach (string act in actions)
            {
                switch (act)
                {
                    case "add":
                        {
                            this.buffer = this.buffer + (char)this.codeFileReader.Read();
                            this.peek = false;
                        }
                        break;
                    case "gc":
                        {
                            if (this.peek)
                            {
                                this.codeFileReader.Read();
                                this.peek = false;
                            }
                            this.ch = this.codeFileReader.Peek();
                            this.peek = true;
                        }
                        break;
                    case "gcnb":
                        {
                             getCharNB();
                             this.peek = true;
                        }
                        break;
                    case "lookup":
                        {

                        }
                        break;
                    case "clr":
                        {
                            this.ch = 0;
                            this.buffer = "";
                            this.codeFileReader.Read();
                            peek = false;
                        }
                        break;
                    default:
                        {
                            outputs = act.Substring(3).Trim('(', ')').Split(',');
                            lexNum = int.Parse(outputs[0]);
                            if (outputs[1].Equals("buffer"))
                            {
                                lexInf = this.buffer;
                            }
                            else
                            {
                                lexInf = outputs[1];
                            }
                        }
                        break;
                }
            }
        }
        /// <summary>
        /// Get one lexem
        /// </summary>
        /// <param name="lexNum">Lexem number</param>
        /// <param name="lexInf">Lexem or error info</param>
        /// <param name="parent">Node object</param>
        public void getLexem(ref int lexNum, ref string lexInf, TreeNode parent)
        {
            string[] conditions;
            bool found = false;
            TreeNode tmp;

            doAction(parent.getData().action, ref lexNum, ref lexInf);
            #region Cikles
            if (parent.Childs.Count == 0 && (!parent.getData().state.Equals("out")))
            {
                tmp = parent.getParent();
                while (true)
                {
                    if (tmp == null)
                    {
                        break;
                    }
                    else if (tmp.getData().state == parent.getData().state)
                    {
                        parent = tmp;
                        break;
                    }
                    tmp = tmp.getParent();
                }
            }
            #endregion
            foreach (TreeNode child in parent.Childs)
            {
                #region OneCharConditions
                if (child.getData().condition.Length == 1)
                {
                    if (child.getData().condition.Equals(((char)this.ch).ToString()))
                    {
                        getLexem(ref lexNum, ref lexInf, child);
                        break;
                    }
                }
                #endregion
                #region LookUp
                else if (parent.getData().state.Equals("lookup"))
                {
                    if(child.getData().condition.Equals("other"))
                    {
                        getLexem(ref lexNum, ref lexInf, child);
                        break;
                    }
                    else if (child.getData().condition.Equals(this.buffer))
                    {
                        getLexem(ref lexNum, ref lexInf, child);
                        break;
                    }
                }
                #endregion
                #region LongConditions
                else
                {
                    conditions = child.getData().condition.Split(';');
                    foreach (string cond in conditions)
                    {
                        switch (cond)
                        {
                            case "digit":
                                {
                                    if ((this.ch >= 48) && (this.ch <= 57))
                                    {
                                        found = true;
                                    }
                                }
                                break;
                            case "letter":
                                {
                                    if (((ch >= 65) && (ch <= 90)) || ((ch >= 97) && (ch <= 122)))
                                    {
                                        found = true;
                                    }
                                }
                                break;
                            case "tab":
                                {
                                    if (this.ch == 9)
                                    {
                                        found = true;
                                    }
                                }
                                break;
                            case "newline":
                                {
                                    if (this.ch == 10)
                                    {
                                        found = true;
                                    }
                                }
                                break;
                            case "carrier":
                                {
                                    if (this.ch == 13)
                                    {
                                        found = true;
                                    }
                                }
                                break;
                            case "endfile":
                                {
                                    if (this.ch == -1)
                                    {
                                        found = true;
                                    }
                                }
                                break;
                            default:
                                {
                                    found = true;
                                }
                                break;
                        }
                        if (found) 
                        {
                            getLexem(ref lexNum, ref lexInf, child);
                            break; 
                        }
                    }
                    if (found) break;
                }
                #endregion
            }

            buffer = "";
        }

        /// <summary>
        /// Sets buffer.
        /// </summary>
        public void setBuffer(string buffer) {
            this.buffer = buffer;
        }

        /// <summary>
        /// Returns states tree.
        /// </summary>
        public Tree getStatesTree() {
            return statesTree;
        }

        static void Main(string[] args) {
            // checks if there is enough arguments
            if (args.Length >= 2) {
                // args[1] - scanner results file name
                StreamWriter lexFileWriter = new StreamWriter(args[1]);

                // args[0] - code example file name
                if (File.Exists(args[0])) {
                    LexemAnalizer scanner = new LexemAnalizer(args[0]);

                    int lexNum;
                    string lexInf;

                    lexFileWriter.WriteLine(" Lexem type number | Lexem information ");

                    do {
                        lexNum = 0;
                        lexInf = "";
                        scanner.getLexem(ref lexNum, ref lexInf, scanner.statesTree.getNodeByState("start"));
                        lexFileWriter.WriteLine(String.Format("{0,18:000}", lexNum) + " | " + lexInf);
                        scanner.buffer = "";
                    }
                    while (lexNum != 0);
                } else {
                    lexFileWriter.WriteLine("Code file does not exist.");
                }

                lexFileWriter.Close();
            } else {
                Console.WriteLine("Please enter code file name as first argument, results file name as second argument.");
            }
        }

        private Tree statesTree;
        private StreamReader codeFileReader;
        private string buffer;
        private int ch;
        private bool peek;
    }
}
